#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>


/* Looking for private ips */
#define PRIVATE_IN_CLASSC(a)            ((((in_addr_t)(a)) & 0xFFFF0000) == 0xc0a80000)
#define PRIVATE_IN_CLASSB(a)            ((((in_addr_t)(a)) & 0xFFF00000) == 0xac100000)
#define PRIVATE_IN_CLASSA(a)            ((((in_addr_t)(a)) & 0x0F000000) == 0x0a000000)


/* Global variables 
 */

/* Control when the ip has been found */
int found=0;

/* Mac address to find */
char *mac_address=NULL;

/*
 * Return true if the ip is private.
 */
inline int is_private(struct in_addr *address)
{
	u_int32_t ip;
	ip = ntohl(address->s_addr);

	if (PRIVATE_IN_CLASSC(ip)){
		printf("Ip privada. (C)\n");
		return 1;
	}
	if (PRIVATE_IN_CLASSA(ip)){
		printf("Ip privada. (A)\n");
		return 1;
	}
	if (PRIVATE_IN_CLASSB(ip)){
		printf("Ip privada. (B)\n");
		return 1;
	}
	return 0;
}


/*
 * dissec an ip packet
 */
inline void handle_ip_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
	const struct ether_header *eth_header; 
	const struct ip *ip_header;

	eth_header = (struct ether_header*) packet;
	ip_header = (struct ip_header*)(packet + sizeof(struct ether_header));

	fprintf(stdout, "Ip source : %s\n", inet_ntoa(ip_header->ip_src));
	is_private(&ip_header->ip_src);
	fprintf(stdout, "Ip destination : %s\n", inet_ntoa(ip_header->ip_dst));
	is_private(&ip_header->ip_dst);
	return;
}

/*
 * dissec an arp packet
 */
void handle_arp_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
	const struct ether_header *eth_header; 
	const struct arphdr *arp_header;
	const struct ether_arp *arp_packet;
	struct in_addr ip_src, ip_dst;
	u_int16_t operation;


	eth_header = (struct ether_header*) packet;
	/* MUST TO BE IMPLEMENTED */
	/* Check the ethernet payload == 28 */
	arp_packet = (struct ether_arp*) (packet + sizeof(struct ether_header));
	arp_header = (struct arphdr*)arp_packet;

	/* I'm only interested in arp request and arp replies */
	operation = ntohs(arp_header->ar_op);
	if (operation != ARPOP_REQUEST && operation != ARPOP_REPLY){
		printf("Unknown Arp operation\n");
		return;
	}
	if (operation == ARPOP_REQUEST){
		printf("Arp request\n");
	}
	else if (operation == ARPOP_REPLY){
		printf("Arp reply\n");
	}
	
	bcopy((void *)arp_packet->arp_spa, (void *) &ip_src, sizeof(struct in_addr));
	bcopy((void *)arp_packet->arp_tpa, (void *) &ip_dst, sizeof(struct in_addr));
	//ip = (struct in_addr*) arp_packet->arp_spa;
	fprintf(stdout,"Sender: mac[%s] ",ether_ntoa(arp_packet->arp_sha));
	fprintf(stdout,"ip[%s]\n",inet_ntoa(ip_src));
	fprintf(stdout,"Destination: mac[%s] ",ether_ntoa(arp_packet->arp_tpa));
	fprintf(stdout,"ip[%s]\n",inet_ntoa(ip_dst));

/*
	printf("Size of struct arphdr : %d\n",sizeof(struct arphdr));
	printf("Size of struct ether_arp :%d\n",sizeof(struct ether_arp));
	printf("Size of struct in_addr: %d\n",sizeof(struct in_addr));
*/

}

/*
 * dissect/print packet
 */
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{

	static int count = 1;                   /* packet counter */
	
	/* declare pointers to packet headers */
	const struct ether_header *eth_header;  /* net/ethernet.h */
	u_int16_t eth_type;

	/* COMPARATION CODE */
	//u_char*  ether_lookingfor=ether_aton(mac_address);

	printf("\nPacket number %d:\n", count);
	count++;
	
	/* define ethernet header */
	eth_header = (struct ether_header*)(packet);
	eth_type = ntohs(eth_header->ether_type);

	fprintf(stdout,"ethernet header source: %s",ether_ntoa(eth_header->ether_shost));
        fprintf(stdout," destination: %s \n",ether_ntoa(eth_header->ether_dhost));
	
	/* PARA FLIPAR */
	// printf("Ethernet source: %s Ethernet target: %s\n",ether_ntoa(eth_header->ether_shost),ether_ntoa(eth_header->ether_dhost));

	if (ntohs(eth_header->ether_type) == ETHERTYPE_ARP){
		printf("Packet type: ARP\n");
		handle_arp_packet(args,header,packet);
		return;
	}
	if (ntohs(eth_header->ether_type) == ETHERTYPE_IP){
		printf("Packet type: IP\n");
		handle_ip_packet(args,header,packet);
		return;
	}

	printf("Packet unknown\n");
	return;

}

void print_error_msg(char *msg)
{
	fprintf(stderr,msg);
	fprintf(stderr,"\n");
	exit(-1);
}

int main(int argc, char **argv)
{
	char *dev = NULL;			/* capture device name */
	char errbuf[PCAP_ERRBUF_SIZE];		/* error buffer */
	pcap_t *handle;				/* packet capture handle */
	char filter_exp[] = "arp"; 		/* filter expression [3] */
	struct bpf_program fp;			/* compiled filter program (expression) */

	if (argc != 3)
		print_error_msg("I need the a network interface and the mac address to find.");

	dev=argv[1];
	mac_address=argv[2];

	/* print capture info */
	printf("Device: %s\n", dev);

	/* open capture device */
	handle = pcap_open_live(dev, BUFSIZ, 1, -1, errbuf);
	if (handle == NULL)
		print_error_msg(errbuf);

	/* make sure we're capturing on an Ethernet device */
	if (pcap_datalink(handle) != DLT_EN10MB)
		print_error_msg("The network interface is not ethernet");

	/* compile the filter expression */
	if (pcap_compile(handle, &fp, filter_exp, 0, 0) == -1)
		print_error_msg(pcap_geterr(handle));
	       
	/* apply the compiled filter */
	if (pcap_setfilter(handle, &fp) == -1)
		print_error_msg(pcap_geterr(handle));

	do {
		/* now we can set our callback function */
		pcap_loop(handle, 1, got_packet,  NULL );
	}
	while(found==0);

	/* cleanup */
	pcap_freecode(&fp);
	pcap_close(handle);

	printf("Program finalized\n");
	return 0;

}

